package model;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Solution {
	Problem problem;
	int[] processes;
	// M X P
	int[][] machinesByProcesses;
	// M X R
	int[][] usedCapacity;
	List<Integer>[] processesListForMachines;
	List<Integer>[] processesListForServices;
	int M;
	int P;
	int R;
	int S;

	public Solution(Problem problem) {
		this.problem = problem;
		M = problem.getMachines().size();
		P = problem.getProcesses().size();
		R = problem.getResources().size();
		S = problem.getServices().size();
		processes = new int[P];
		machinesByProcesses = new int[M][P];
		processesListForMachines = new List[M];
		processesListForServices = new List[S];
		for (int i = 0; i < M; i++) {
			processesListForMachines[i] = new ArrayList<Integer>();
		}
		for (int i = 0; i < S; i++) {
			processesListForServices[i] = new ArrayList<Integer>();
		}
		usedCapacity = new int[M][R];
	}

	public void assignProcessToMachine(int p_index, int m_index) {
		int s_index = problem.servicesOfProcesses[p_index];
		processes[p_index] = m_index;
		machinesByProcesses[m_index][p_index] = 1;
		processesListForMachines[m_index].add(p_index);
		processesListForServices[s_index].add(p_index);
		for (int j = 0; j < R; j++) {
			usedCapacity[m_index][j] += problem.processesByResourcesRequirement[p_index][j];
		}
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		for (int m_i = 0; m_i < processesListForMachines.length; m_i++) {
			sb.append(String.format("Machine %d --> %s\n", m_i,
					processesListForMachines[m_i]));
		}
		return sb.toString();
	}

	public boolean isFeasible() {
		if (!isFeasibleForCapacityConstraints())
			return false;
		if (!isFeasibleForConflictConstraints())
			return false;
		if (!isFeasibleForSpreadConstraints())
			return false;
		if (!isFeasibleForDependencyConstraints())
			return false;
		if (!isFeasibleForTransientUsageConstraints())
			return false;
		return true;
	}

	public boolean isFeasibleForCapacityConstraints() {
		for (int i = 0; i < M; i++)
			for (int j = 0; j < R; j++)
				if (usedCapacity[i][j] > problem.machinesByResourcesCapacity[i][j]) {
					System.out
							.printf("Not feasible. machine:%d resource:%d (needed:%d available:%d)\n",
									i, j, usedCapacity[i][j],
									problem.machinesByResourcesCapacity[i][j]);
					return false;
				}
		return true;
	}

	public boolean isFeasibleForConflictConstraints() {
		for (int s_index = 0; s_index < S; s_index++) {
			Set<Integer> aSet = new HashSet<Integer>();
			for (Integer p_index : processesListForServices[s_index]) {
				aSet.add(processes[p_index]);
			}
			if (aSet.size() < processesListForServices[s_index].size()) {
				System.out
						.printf("Not feasible. Processes in the same service should not be scheduled in the same machine");
				return false;
			}
		}
		return true;
	}

	public boolean isFeasibleForSpreadConstraints() {
		for (int s_index = 0; s_index < S; s_index++) {
			Set<Integer> aSet = new HashSet<Integer>();
			for (Integer p_index : processesListForServices[s_index]) {
				int m_index = processes[p_index];
				aSet.add(problem.machines_locations[m_index]);
			}
			if (aSet.size() < problem.services.get(s_index).spreadMin) {
				System.out.println("Not feasible.");
				return false;
			}
		}
		return true;
	}

	public boolean isFeasibleForDependencyConstraints() {
		for (int s_index = 0; s_index < S; s_index++) {
			if (problem.service_dependencies[s_index].isEmpty())
				continue;
			Set<Integer> neighborhoods = new HashSet<Integer>();
			for (Integer service_dep : problem.service_dependencies[s_index]) {
				for (Integer p_index : processesListForServices[service_dep]) {
					int m_index = processes[p_index];
					neighborhoods.add(problem.machines_neighborhoods[m_index]);
				}
			}
			for (Integer p_index : processesListForServices[s_index]) {
				int m_index = processes[p_index];
				int n_i = problem.machines_neighborhoods[m_index];
				if (!neighborhoods.contains(n_i)) {
					System.out.printf("Neighborhoods %s (%d - %d) \n",
							neighborhoods, m_index, n_i);
					return false;
				}
			}
		}
		return true;
	}

	public boolean isFeasibleForTransientUsageConstraints() {
		return true;
	}

}
